/**
 * Copyright 2022 Beijing Douyin Information Service Co., Ltd.
 * SPDX-License-Identifier: BSD-3-Clause
 * <p>
 * Package com.bytedance.microapp Sign 签名算法
 * <p>
 * Package com.bytedance.microapp Sign implement the signature algorithm
 */

package com.cc.dy.util;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class SignUtil {

    /**
     * 担保支付请求不参与签名参数
     * app_id 小程序appID
     * thirdparty_id 代小程序进行该笔交易调用的第三方平台服务商id
     * sign 签名
     * other_settle_params 其他分账方参数
     * <p>
     * Guaranteed payment requests do not participate in signature parameters
     * app_id Applets appID
     * thirdparty_id The id of the third-party platform service provider that calls the transaction on behalf of the Applets
     * sign sign
     * other_settle_params Other settle params
     */
    public final static List<String> REQUEST_NOT_NEED_SIGN_PARAMS = Arrays.asList("app_id", "thirdparty_id", "sign", "other_settle_params", "salt");


    /**
     * RequestSign 担保支付请求签名算法
     *
     * @param paramsMap {@code Map<String, Object>} 所有的请求参数
     * @param salt      支付密钥值，需要替换为自己的密钥(完成进件后，开发者可在字节开放平台-【某小程序】-【功能】-【支付】-【担保交易设置】中查看支付系统秘钥 SALT)
     * @return 签名字符串 RequestSign Guaranteed payment request signature algorithm
     */
    public static String requestSign(Map<String, Object> paramsMap, String salt) {
        List<String> paramsArr = new ArrayList<>();
        for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {
            String key = entry.getKey();
            if (REQUEST_NOT_NEED_SIGN_PARAMS.contains(key)) {
                continue;
            }
            String value = entry.getValue().toString();

            value = value.trim();
            if (value.startsWith("\"") && value.endsWith("\"") && value.length() > 1) {
                value = value.substring(1, value.length() - 1);
            }
            value = value.trim();
            if (value.equals("") || value.equals("null")) {
                continue;
            }
            paramsArr.add(value);
        }
        paramsArr.add(salt);
        Collections.sort(paramsArr);
        StringBuilder signStr = new StringBuilder();
        String sep = "";
        for (String s : paramsArr) {
            signStr.append(sep).append(s);
            sep = "&";
        }
        return md5FromStr(signStr.toString());
    }

    /**
     * CallbackSign 担保支付回调签名算法
     *
     * @param params {@code List<String>} 所有字段（验证时注意不包含 sign 签名本身，不包含空字段与 type 常量字段）内容与平台上配置的 token
     * @return 签名字符串 CallbackSign Guaranteed payment callback signature algorithm
     */
    public static String callbackSign(List<String> params) {
        try {
            String concat = params.stream().sorted().collect(Collectors.joining(""));
            byte[] arrayByte = concat.getBytes(StandardCharsets.UTF_8);
            MessageDigest mDigest = MessageDigest.getInstance("SHA1");
            byte[] digestByte = mDigest.digest(arrayByte);

            StringBuilder signBuilder = new StringBuilder();
            for (byte b : digestByte) {
                signBuilder.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
            }
            return signBuilder.toString();
        } catch (Exception exp) {
            return "";
        }
    }

    /**
     * md5FromStr md5算法对该字符串计算摘要
     *
     * @param inStr {@code String} 需要签名的字符串
     * @return 签名字符串 md5FromStr The md5 algorithm computes a digest of the string
     */
    private static String md5FromStr(String inStr) {
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return "";
        }

        byte[] byteArray = inStr.getBytes(StandardCharsets.UTF_8);
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuilder hexValue = new StringBuilder();
        for (byte md5Byte : md5Bytes) {
            int val = ((int) md5Byte) & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }
}
